package com.seleniti.enpareja.android.ui.view;

import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;

import org.ioc.commons.impl.android.common.util.ArrayAdapterAsList;
import org.ioc.commons.impl.android.common.util.OnValueChangeTextWatcher;
import org.ioc.commons.impl.android.common.util.SaveInstanceStateHelper;
import org.ioc.commons.impl.android.common.util.SaveInstanceStateHelper.NullBehaviour;
import org.ioc.commons.impl.android.ui.HasEnabledWrapper;
import org.ioc.commons.impl.android.ui.HasTextWrapper;
import org.ioc.commons.impl.android.ui.HasValueWrapper;
import org.ioc.commons.ui.HasText;
import org.ioc.commons.ui.HasValue;
import org.ioc.commons.ui.impl.HasValueAdapter;
import org.ioc.commons.ui.impl.OnChangeCommand;
import org.ioc.commons.utils.Format;
import org.ioc.commons.utils.FormatterLogger;

import android.app.DatePickerDialog;
import android.app.DatePickerDialog.OnDateSetListener;
import android.app.Dialog;
import android.content.Context;
import android.content.res.TypedArray;
import android.database.DataSetObserver;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v4.view.MenuItemCompat;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener;
import android.support.v7.widget.ShareActionProvider;
import android.text.Editable;
import android.text.Html;
import android.text.Spanned;
import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AbsListView.MultiChoiceModeListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.Checkable;
import android.widget.CheckedTextView;
import android.widget.DatePicker;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;

import com.google.android.gms.drive.internal.aa;
import com.seleniti.enpareja.android.R;
import com.seleniti.enpareja.controller.common.domain.LoggedUserInfo;
import com.seleniti.enpareja.controller.model.integration.database.domain.Food;
import com.seleniti.enpareja.controller.ui.view.MealsView;
import com.seleniti.enpareja.controller.ui.view.MealsView.Action;
import com.seleniti.enpareja.controller.ui.view.MealsView.Event;
import com.seleniti.enpareja.controller.ui.view.MealsView.Operation;
import com.seleniti.enpareja.controller.ui.view.MealsView.Presenter;
import com.seleniti.enpareja.controller.ui.view.MealsViewPresenter;

public class MealsViewWidget extends EnParejaBaseView<Event, Operation, Action, Presenter> implements MealsView {

	private static final FormatterLogger logger = FormatterLogger.getLogger(MealsViewWidget.class);

	private Menu mMenu;
	private boolean loading;
	private boolean enabled;
	private ActionMode actionMode;
	private ArrayAdapterAsList<Food> aadapter;
	private ListView cellList;
	private final Set<Food> selection = new HashSet<>();
	private final Set<DoubleSelection> doubleSelection = new HashSet<>();
	private SwipeRefreshLayout swipeLayout;
	private TextView date;
	private View form;
	private View list;
	private Button btnOk;
	private Button btnCancel;
	private TextView lunch;
	private TextView dinner;
	private TextView filterFrom;
	private TextView filterTo;

	protected boolean lunchTouched;

	public MealsViewWidget() {
		super(true);
	}

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		this.setHasOptionsMenu(true);
		this.setRetainInstance(true);
	}

	@SuppressWarnings("unchecked")
	@Override
	public void onActivityCreated(Bundle savedInstanceState) {
		super.onActivityCreated(savedInstanceState);
		this.presenter = new MealsViewPresenter(this, eventbus, operationManager, actionController);

		if (savedInstanceState != null) {
			SaveInstanceStateHelper ss = new SaveInstanceStateHelper(this.getClass().getName(), savedInstanceState);

			ss.restore().value(NullBehaviour.KEEP_VALUE, this.boardShown()).value(this.loggedUserInfo())
					.value(this.date()).text(this.lunch()).text(this.dinner());
		}
	}

	final class MealsDatePicker extends android.support.v4.app.DialogFragment {
		private final HasValue<Date> filter;

		MealsDatePicker(HasValue<Date> filter) {
			this.filter = filter;
		}

		@Override
		public Dialog onCreateDialog(Bundle savedInstanceState) {
			// Use the current date as the default date in the
			// picker
			final Calendar c = Calendar.getInstance();

			Date d = filter.getValue();
			if (d != null) {
				c.setTime(d);
			}

			int year = c.get(Calendar.YEAR);
			int month = c.get(Calendar.MONTH);
			int day = c.get(Calendar.DAY_OF_MONTH);

			// Create a new instance of DatePickerDialog and return
			// it
			return new DatePickerDialog(getActivity(), new OnDateSetListener() {

				@Override
				public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
					c.set(Calendar.YEAR, year);
					c.set(Calendar.MONTH, monthOfYear);
					c.set(Calendar.DAY_OF_MONTH, dayOfMonth);

					filter.setValue(c.getTime());
				}
			}, year, month, day);
		}
	}

	private static class ItemViewHolder {

		protected TextView caption;
		protected CheckedTextView lunch;
		protected CheckedTextView dinner;
	}

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
		View view = inflater.inflate(R.layout.meals_view, container, false);

		this.swipeLayout = (SwipeRefreshLayout) view.findViewById(R.id.swipe_container);
		this.swipeLayout.setOnRefreshListener(new OnRefreshListener() {

			@Override
			public void onRefresh() {
				presenter.onRefreshData();
			}
		});
		this.swipeLayout.setColorSchemeResources(android.R.color.holo_blue_bright, android.R.color.holo_green_light,
				android.R.color.holo_orange_light, android.R.color.holo_red_light);

		this.date = (TextView) view.findViewById(R.id.date);
		this.btnOk = (Button) view.findViewById(R.id.btnOk);
		this.btnCancel = (Button) view.findViewById(R.id.btnCancel);
		this.lunch = (TextView) view.findViewById(R.id.lunch);
		this.dinner = (TextView) view.findViewById(R.id.dinner);
		this.filterFrom = (TextView) view.findViewById(R.id.filterFrom);
		this.filterTo = (TextView) view.findViewById(R.id.filterTo);

		this.filterFrom.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				android.support.v4.app.DialogFragment picker = new MealsDatePicker(filterFromDate());

				picker.show(getFragmentManager(), "filterFromDatePicker");
			}
		});

		this.filterTo.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				android.support.v4.app.DialogFragment picker = new MealsDatePicker(filterToDate());

				picker.show(getFragmentManager(), "filterToDatePicker");
			}
		});

		this.filterFrom.addTextChangedListener(new OnValueChangeTextWatcher() {

			@Override
			public void onValueChanged(Editable s) {
				presenter.onDateFilterChanged();
			}

		});

		this.filterTo.addTextChangedListener(new OnValueChangeTextWatcher() {

			@Override
			public void onValueChanged(Editable s) {
				presenter.onDateFilterChanged();
			}

		});

		this.list = (View) view.findViewById(R.id.list);
		this.form = (View) view.findViewById(R.id.form);

		this.btnOk.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				saveModificationForm();
			}
		});

		this.btnCancel.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				hideModificationForm();
			}
		});

		// Calendar cal = Calendar.getInstance();
		//
		// this.filterFrom.init(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH),
		// cal.get(Calendar.DAY_OF_MONTH), this);
		// this.filterTo.init(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH),
		// cal.get(Calendar.DAY_OF_MONTH), this);

		this.cellList = (ListView) view.findViewById(R.id.cellList);
		this.cellList.setEmptyView(view.findViewById(R.id.empty));
		Context context = this.getContext();
		if (this.aadapter == null || !Objects.equals(this.aadapter.getContext(), this.getContext())) {

			List<Food> objects = (this.aadapter == null) ? new ArrayList<Food>() : this.aadapter;

			this.aadapter = new ArrayAdapterAsList<Food>(context, R.layout.meals_element, objects) {
				@Override
				public View getView(int position, View convertView, ViewGroup parent) {
					ItemViewHolder ivh = null;
					LinearLayout panel = null;
					Food item = this.getItem(position);

					if (getActionMode() == ActionMode.SWITCH_MEALS) {
						convertView = null;
					}

					if (convertView == null) {
						Bundle savedInstanceState = new Bundle();
						LayoutInflater inflater = getLayoutInflater(savedInstanceState);
						ivh = new ItemViewHolder();

						if (actionMode == ActionMode.DELETE_ELEMENT) {
							convertView = inflater.inflate(android.R.layout.simple_list_item_multiple_choice, parent,
									false);

							CheckedTextView ctv = (CheckedTextView) convertView;

							ivh.caption = ctv;

						} else {
							convertView = inflater.inflate(R.layout.meals_element, parent, false);
						}

						if (convertView instanceof LinearLayout) {
							panel = (LinearLayout) convertView;

							ivh.caption = (TextView) panel.findViewById(R.id.caption);
							ivh.lunch = (CheckedTextView) panel.findViewById(R.id.lunch_meal);
							ivh.dinner = (CheckedTextView) panel.findViewById(R.id.dinner_meal);

							if (getActionMode() == ActionMode.SWITCH_MEALS) {
								int[] attrs = { android.R.attr.listChoiceIndicatorMultiple };
								TypedArray ta = getContext().getTheme().obtainStyledAttributes(attrs);
								Drawable indicator1 = ta.getDrawable(0);
								Drawable indicator2 = ta.getDrawable(0);

								ivh.lunch.setCheckMarkDrawable(indicator1);
								ivh.dinner.setCheckMarkDrawable(indicator2);

								ivh.lunch.setOnClickListener(new OnClickListener() {

									@Override
									public void onClick(View v) {
										if (v instanceof Checkable) {
											int position = (int) v.getTag();
											Food item = getItem(position);

											((Checkable) v).setChecked(!((Checkable) v).isChecked());
											// cellList.setItemChecked(position,
											// !cellList.isItemChecked(position));

											DoubleSelection meal = newDoubleSelection(item, false);

											if (((Checkable) v).isChecked()) {
												doubleSelection.add(meal);
											} else {
												doubleSelection.remove(meal);
											}

											presenter.onDoubleSelectionChanged();
										}
									};
								});

								ivh.dinner.setOnClickListener(new OnClickListener() {

									@Override
									public void onClick(View v) {
										if (v instanceof Checkable) {
											int position = (int) v.getTag();
											Food item = getItem(position);
											((Checkable) v).setChecked(!((Checkable) v).isChecked());
											// cellList.setItemChecked(position,
											// !cellList.isItemChecked(position));

											DoubleSelection meal = newDoubleSelection(item, true);

											if (((Checkable) v).isChecked()) {
												doubleSelection.add(meal);
											} else {
												doubleSelection.remove(meal);
											}

											presenter.onDoubleSelectionChanged();
										}
									};
								});

								ta.recycle();
							} else {
								ivh.lunch.setCheckMarkDrawable(null);
								ivh.dinner.setCheckMarkDrawable(null);
							}

						}
						convertView.setTag(ivh);
					} else {
						ivh = (ItemViewHolder) convertView.getTag();
					}

					DateFormat dtf = DateFormat.getDateInstance(DateFormat.FULL);
					boolean today = item.getDate() != null ? DateUtils.isToday(item.getDate().getTime()) : false;

					Spanned header = Html
							.fromHtml(item.getDate() != null ? (today ? "<b><font color='#FF7F50'>HOY </font></b>" : "")
									+ Format.capitalize(dtf.format(item.getDate()))
									: "-- sin fecha --");
					String lunchStr = item.getLunch() != null ? item.getLunch() : "";
					String dinnerStr = item.getDinner() != null ? item.getDinner() : "";

					ivh.caption.setText(header);
					if (ivh.lunch != null) {
						ivh.lunch.setText(lunchStr);
						ivh.lunch.setTag(position);
						DoubleSelection meal = newDoubleSelection(item, false);
						ivh.lunch.setChecked(doubleSelection.contains(meal));
					}
					if (ivh.dinner != null) {
						ivh.dinner.setText(dinnerStr);
						ivh.dinner.setTag(position);
						DoubleSelection meal = newDoubleSelection(item, true);
						ivh.dinner.setChecked(doubleSelection.contains(meal));
					}

					if (today) {
						convertView.setBackgroundColor(Color.parseColor("#F0F0C0"));
					} else {
						convertView.setBackgroundColor(Color.TRANSPARENT);
					}

					return convertView;
				}
			};
		}

		this.aadapter.registerDataSetObserver(new DataSetObserver() {

			@Override
			public void onChanged() {
				super.onChanged();

				if (getActivity() != null) {
					setShareIntent(new SetShareIntentParameter<Food>(getNonEmptyMeals(aadapter),
							new MealsStringFormatter(), getString(R.string.meals)));
				}

				clearDoubleSelection();
			}
		});

		this.cellList.setAdapter(aadapter);
		this.cellList.setChoiceMode(ListView.CHOICE_MODE_NONE);
		this.cellList.setOnItemClickListener(new OnItemClickListener() {

			@Override
			public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
				if (actionMode != null) {
					Food value = (Food) cellList.getItemAtPosition(position);
					switch (actionMode) {
					case MODIFY_ELEMENT:
						showModifyForm(value);
						break;
					case DELETE_ELEMENT:
						boolean selected = !cellList.isItemChecked(position);
						cellList.setItemChecked(position, selected);

						if (selected) {
							selection.add((Food) cellList.getItemAtPosition(position));
						} else {
							selection.remove((Food) cellList.getItemAtPosition(position));
						}

						presenter.onSingleSelectionChanged();
						break;
					default:
						/*
						 * Nothing to do
						 */
					}
				}
			}
		});

		// Capture ListView item click
		this.cellList.setMultiChoiceModeListener(new MultiChoiceModeListener() {

			@Override
			public boolean onCreateActionMode(android.view.ActionMode mode, Menu menu) {
				return false;
			}

			@Override
			public boolean onPrepareActionMode(android.view.ActionMode mode, Menu menu) {
				return false;
			}

			@Override
			public boolean onActionItemClicked(android.view.ActionMode mode, MenuItem item) {
				return false;
			}

			@Override
			public void onDestroyActionMode(android.view.ActionMode mode) {

			}

			@Override
			public void onItemCheckedStateChanged(android.view.ActionMode mode, int position, long id, boolean checked) {

			}

		});

		return view;
	}

	private void clearDoubleSelection() {
		if (!doubleSelection.isEmpty()) {
			doubleSelection.clear();

			repaint();

			presenter.onDoubleSelectionChanged();
		}

	}

	class MealsStringFormatter implements Format.StringFormatter<Food> {

		@Override
		public String asString(Food obj) {
			return obj != null ? Format.substitute("{0} Almuerzo: {1} Cena: {2}", obj.getDate() != null ? DateFormat
					.getDateInstance(DateFormat.FULL).format(obj.getDate()) : "",
					obj.getLunch() != null ? obj.getLunch() : "--", obj.getDinner() != null ? obj.getDinner() : "--")
					: "";
		}
	}

	@Override
	public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
		super.onCreateOptionsMenu(menu, inflater);

		inflater.inflate(R.menu.meals_menu, menu);

		this.mMenu = menu;

		MenuItem btnSwitch = mMenu.findItem(R.id.switch_item);
		MenuItem btnDelete = mMenu.findItem(R.id.delete_item);
		// Locate MenuItem with ShareActionProvider
		MenuItem btnShare = menu.findItem(R.id.menu_item_share);

		// Fetch and store ShareActionProvider
		this.mShareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(btnShare);
		setShareIntent(new SetShareIntentParameter<Food>(getNonEmptyMeals(this.aadapter), new MealsStringFormatter(),
				getString(R.string.meals)));
		btnShare.setVisible(this.mShareActionProvider != null);

		btnSwitch.setChecked(this.actionMode == ActionMode.SWITCH_MEALS);
		btnSwitch.setIcon(btnSwitch.isChecked() ? R.drawable.switch_icon_on : R.drawable.switch_icon);

		btnDelete.setChecked(this.actionMode == ActionMode.DELETE_ELEMENT);
		btnDelete.setIcon(btnDelete.isChecked() ? R.drawable.bin_icon_on : R.drawable.bin_icon);

	}

	private List<Food> getNonEmptyMeals(List<Food> all) {
		List<Food> nonEmpty = new ArrayList<>();
		for (Food meal : all) {
			if ((meal.getLunch() != null && !meal.getLunch().isEmpty())
					|| (meal.getDinner() != null && !meal.getDinner().isEmpty())) {
				nonEmpty.add(meal);
			}
		}
		return nonEmpty;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case R.id.switch_item:
			this.setActionMode(this.getActionMode() != ActionMode.SWITCH_MEALS ? ActionMode.SWITCH_MEALS
					: ActionMode.MODIFY_ELEMENT);
			return true;
		case R.id.delete_item:
			this.setActionMode(this.getActionMode() != ActionMode.DELETE_ELEMENT ? ActionMode.DELETE_ELEMENT
					: ActionMode.MODIFY_ELEMENT);
			return true;
		default:
			this.setActionMode(ActionMode.MODIFY_ELEMENT);
			return super.onOptionsItemSelected(item);
		}
	}

	@Override
	public void setLoading(boolean loading) {
		this.loading = loading;
		if (mMenu != null) {
			MenuItem btnSwitch = mMenu.findItem(R.id.switch_item);
			MenuItem btnDelete = mMenu.findItem(R.id.delete_item);

			btnSwitch.setEnabled(!loading);
			btnDelete.setEnabled(!loading);
		}

		if (this.swipeLayout != null) {
			this.swipeLayout.setRefreshing(loading);
		}
	}

	@Override
	public boolean isLoading() {
		return this.loading;
	}

	@Override
	public boolean isEnabled() {
		return this.enabled;
	}

	@Override
	public void setEnabled(boolean enabled) {
		this.enabled = enabled;
		if (mMenu != null) {
			MenuItem btnSwitch = mMenu.findItem(R.id.switch_item);
			MenuItem btnDelete = mMenu.findItem(R.id.delete_item);

			HasEnabledWrapper.setEnabled(enabled, btnDelete, btnSwitch);
		}
	}

	@Override
	public void setActionMode(ActionMode mode) {
		boolean changed = !Objects.equals(this.actionMode, mode);
		boolean requiresRepaint = requiresRepaint(mode, actionMode, ActionMode.DELETE_ELEMENT, ActionMode.SWITCH_MEALS);

		this.actionMode = mode;

		if (mode != null) {
			switch (mode) {
			case MODIFY_ELEMENT:
				this.cellList.setChoiceMode(ListView.CHOICE_MODE_NONE);
				break;
			case SWITCH_MEALS:
				this.cellList.setChoiceMode(ListView.CHOICE_MODE_NONE);
				break;
			case DELETE_ELEMENT:
				this.cellList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
				break;
			}
		}

		if (this.getActivity() != null) {
			this.getActivity().invalidateOptionsMenu();
		}

		if (requiresRepaint && this.cellList != null) {
			repaint();
		}

		if (changed) {
			this.presenter.onActionModeChanged();
		}

	}

	private void repaint() {
		this.saveScrollPosition(cellList);

		/*
		 * Clear all recycled views, so they will be repainted properly
		 */
		this.cellList.setAdapter(aadapter);

		this.restoreScrollPosition(cellList);
	}

	@Override
	public ActionMode getActionMode() {
		return this.actionMode;
	}

	@Override
	public HasValue<Board> boardShown() {
		return HasValueAdapter.<Board> forField("boardShown", this).onChange(new OnChangeCommand<Board>() {

			@Override
			public void execute(Board value) {
				if (value == null) {
					value = Board.MAIN_LIST;
				}

				switch (value) {
				case EDIT_FORM:
					list.setVisibility(View.GONE);
					form.setVisibility(View.VISIBLE);
					lunch.requestFocus();
					// TODO: Slide effect
					break;
				case MAIN_LIST:
					list.setVisibility(View.VISIBLE);
					form.setVisibility(View.GONE);
					// TODO: Slide effect
					break;
				default:
					throw new IllegalArgumentException("Unknown board: " + value);
				}

			}
		});
	}

	@Override
	public boolean goBoardBack() {
		return this.presenter.onGoBoardBack();
	}

	@Override
	public List<Food> foodsList() {
		return this.aadapter;
	}

	@Override
	public void showModifyForm(Food element) {
		Date date = element.getDate();

		date().setValue(date);
		lunch().setText(element.getLunch());
		dinner().setText(element.getDinner());
		boardShown().setValue(Board.EDIT_FORM);
	}

	@Override
	public void saveModificationForm() {
		this.presenter.onMealModified();
	}

	@Override
	public void hideModificationForm() {
		boardShown().setValue(Board.MAIN_LIST);
	}

	@Override
	public HasValue<Date> date() {
		return HasValueAdapter.<Date> forField("date", this).onChange(new OnChangeCommand<Date>() {

			@Override
			public void execute(Date value) {
				date.setText(value != null ? DateFormat.getDateInstance().format(value) : "--");
			}
		});
	}

	@Override
	public HasValue<Date> filterFromDate() {
		return HasValueWrapper.from(filterFrom, this).asDate();
	}

	@Override
	public HasValue<Date> filterToDate() {
		return HasValueWrapper.from(filterTo, this).asDate();
	}

	@Override
	public HasText dinner() {
		return HasTextWrapper.from(this.dinner, this);
	}

	@Override
	public HasText lunch() {
		return HasTextWrapper.from(this.lunch, this);
	}

	@Override
	public Set<Food> getSingleSelection() {
		return selection;
	}

	@Override
	public Set<DoubleSelection> getDoubleSelection() {
		return doubleSelection;
	}

	@SuppressWarnings("unchecked")
	@Override
	public void onSaveInstanceState(Bundle outState) {
		super.onSaveInstanceState(outState);

		SaveInstanceStateHelper ss = new SaveInstanceStateHelper(this.getClass().getName(), outState);

		ss.save().value(this.boardShown()).value(this.loggedUserInfo()).value(this.date()).text(this.lunch())
				.text(this.dinner());
	}

	@Override
	public HasValue<LoggedUserInfo> loggedUserInfo() {
		return HasValueAdapter.<LoggedUserInfo> forField("loggedUserInfo", this);
	}

	private DoubleSelection newDoubleSelection(Food item, boolean dinner) {
		DoubleSelection meal = new DoubleSelection();
		meal.setElement(item);
		meal.setDinner(dinner);
		return meal;
	}
}
